using System.Collections.Generic;

using UnityEngine;
using UnityEngine.EventSystems;


namespace NL.Advanced
{

    public interface IInputMask
    {
        Transform transform { get; }
    }
    public interface IInputMaskHole : IInputMask, IPointerClickHandler,
         IPointerDownHandler, IPointerUpHandler, IPointerMoveHandler,
        IDragHandler, IBeginDragHandler, IEndDragHandler
    {
        void SetNextUnMask(GameObject go);
        GameObject gameObject { get; }
    }
    public class GOTStandaloneInputModule : StandaloneInputModule
    {
        private static GOTStandaloneInputModule INSTANCE;

        protected override void Awake()
        {
            base.Awake();

            INSTANCE = this;
        }

        protected override void OnDestroy()
        {
            base.OnDestroy();

            INSTANCE = null;
        }

        public GameObject GameObjectUnderPointer()
        {
            PointerEventData eventData = GetLastPointerEventData(PointerInputModule.kMouseLeftId);

            if (eventData != null && eventData.pointerEnter)
            {
                LOG.Add($"[GOTStandaloneInputModule.GameObjectUnderPointer()]1：{eventData.pointerEnter}");
                return eventData.pointerEnter.gameObject;
            }

            //鼠标
            var go = GetCurrentFocusedGameObject();
            if (go != null)
            {

                LOG.Add($"[GOTStandaloneInputModule.GameObjectUnderPointer()]2：{go}");
                return go;
            }


            //触摸
            for (int i = 0; i < input.touchCount; ++i)
            {
                Touch touch = input.GetTouch(i);

                if (touch.type == TouchType.Indirect)
                    continue;

                bool released;
                bool pressed;
                var data = GetTouchPointerEventData(touch, out pressed, out released);

                if (data != null && data.pointerCurrentRaycast.gameObject != null)
                {
                    LOG.Add($"[GOTStandaloneInputModule.GameObjectUnderPointer()]3：{data.pointerCurrentRaycast.gameObject}");

                    return data.pointerCurrentRaycast.gameObject;
                }
            }

            return null;
        }


        static GameObject _FindNoMaskIn(List<RaycastResult> candidates)
        {
            if (candidates == null || candidates.Count < 1)
                return null;

            foreach (var a in candidates)
            {
                if (a.gameObject != null && a.gameObject.GetComponent<IInputMask>() != null)
                    continue;
                return a.gameObject;
            }
            return null;
        }

        /// <summary>
        /// 新得 射线碰撞集合排序算法...忽略  选定得 层...
        /// </summary>
        /// <param name="candidates"></param>
        /// <returns></returns>
        protected static new RaycastResult FindFirstRaycast(List<RaycastResult> candidates)
        {
            if (candidates == null || candidates.Count < 1) return new RaycastResult();


            var candidatesCount = candidates.Count;


            //过滤 掉..IEventProtecter
            for (int i = candidatesCount - 1; i >= 0; i--)
            {
                IEventProtecter prot = candidates[i].gameObject != null ? candidates[i].gameObject.GetComponentInParent<IEventProtecter>() : null;

                if (prot != null)
                {
                    candidates.RemoveAt(i);
                }
            }



            IInputMaskHole _maskhole = null;

            //如果存在 IInputMaskHole 告诉它  谁是它遮挡得对象.
            if (candidates.Count > 0 && candidates[0].gameObject != null)
            {
                _maskhole = candidates[0].gameObject.GetComponent<IInputMaskHole>();

                if (_maskhole != null)
                {

                    GameObject unmask = _FindNoMaskIn(candidates);

                    _maskhole.SetNextUnMask(unmask);

                    LOG.Add($"[GOTStandaloneInputModule.FindFirstRaycast]:{_maskhole}  ; unmask={unmask}");

                }


            }

            RaycastResult _InputIgnoreHoleRaycast = default(RaycastResult);
            RaycastResult _InputIgnoreMaskRaycast = default(RaycastResult);
            if (candidates.Count > 0)
            {

                foreach (var candidate in candidates)
                {
                    if (candidate.gameObject.GetComponentInParent<IInputIgnoreHole>() != null)
                    {
                        if (_InputIgnoreHoleRaycast.isValid == false)
                            _InputIgnoreHoleRaycast = candidate;

                        //Debug.Log($"[GOTStandaloneInputModule.FindFirstRaycast]:{_InputIgnoreHoleRaycast}  ; Hole={candidate.gameObject.GetComponentInParent<IInputIgnoreHole>()}");
                    }

                    if (candidate.gameObject.GetComponent<IInputIgnoreMask>() != null)
                    {
                        _InputIgnoreMaskRaycast = candidate;

                        //Debug.Log($"[GOTStandaloneInputModule.FindFirstRaycast]:{_InputIgnoreMaskRaycast}  ; InputIgnoreMask={candidate.gameObject.GetComponent<IInputIgnoreMask>()}");
                    }
                }
            }

            if (_InputIgnoreHoleRaycast.isValid && _InputIgnoreMaskRaycast.isValid)
                return _InputIgnoreHoleRaycast;
            else if (_InputIgnoreMaskRaycast.isValid)
                return _InputIgnoreMaskRaycast;

            return candidates.Count > 0 ? candidates[0] : new RaycastResult();

        }


        private bool ShouldIgnoreEventsOnNoFocus()
        {
#if UNITY_EDITOR
            return !UnityEditor.EditorApplication.isRemoteConnected;
#else
            return true;
#endif
        }
        public override void Process()
        {
            if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus())
                return;

            bool usedEvent = SendUpdateEventToSelectedObject();

            // case 1004066 - touch / mouse events should be processed before navigation events in case
            // they change the current selected gameobject and the submit button is a touch / mouse button.

            // touch needs to take precedence because of the mouse emulation layer
            if (!ProcessTouchEvents() && input.mousePresent)
                ProcessMouseEvent();

            if (eventSystem.sendNavigationEvents)
            {
                if (!usedEvent)
                    usedEvent |= SendMoveEventToSelectedObject();

                if (!usedEvent)
                    SendSubmitEventToSelectedObject();
            }
        }
        private bool ProcessTouchEvents()
        {
            for (int i = 0; i < input.touchCount; ++i)
            {
                Touch touch = input.GetTouch(i);

                if (touch.type == TouchType.Indirect)
                    continue;

                bool released;
                bool pressed;
                var pointer = GetTouchPointerEventData(touch, out pressed, out released);

                ProcessTouchPress(pointer, pressed, released);

                if (!released)
                {
                    ProcessMove(pointer);
                    ProcessDrag(pointer);
                }
                else
                    RemovePointerData(pointer);
            }
            return input.touchCount > 0;
        }
        protected new PointerEventData GetTouchPointerEventData(Touch input, out bool pressed, out bool released)
        {
            PointerEventData pointerData;
            var created = GetPointerData(input.fingerId, out pointerData, true);

            pointerData.Reset();

            pressed = created || (input.phase == TouchPhase.Began);
            released = (input.phase == TouchPhase.Canceled) || (input.phase == TouchPhase.Ended);

            if (created)
                pointerData.position = input.position;

            if (pressed)
                pointerData.delta = Vector2.zero;
            else
                pointerData.delta = input.position - pointerData.position;

            pointerData.position = input.position;

            pointerData.button = PointerEventData.InputButton.Left;

            if (input.phase == TouchPhase.Canceled)
            {
                pointerData.pointerCurrentRaycast = new RaycastResult();
            }
            else
            {
                eventSystem.RaycastAll(pointerData, m_RaycastResultCache);

                var raycast = FindFirstRaycast(m_RaycastResultCache);
                pointerData.pointerCurrentRaycast = raycast;
                m_RaycastResultCache.Clear();
            }

            pointerData.pressure = input.pressure;
            pointerData.altitudeAngle = input.altitudeAngle;
            pointerData.azimuthAngle = input.azimuthAngle;
            pointerData.radius = Vector2.one * input.radius;
            pointerData.radiusVariance = Vector2.one * input.radiusVariance;

            return pointerData;
        }

        private readonly MouseState m_MouseState = new MouseState();
        protected override MouseState GetMousePointerEventData(int id)
        {
            PointerEventData leftData;
            var created = GetPointerData(kMouseLeftId, out leftData, true);

            leftData.Reset();

            if (created)
                leftData.position = input.mousePosition;

            Vector2 pos = input.mousePosition;
            if (Cursor.lockState == CursorLockMode.Locked)
            {
                // We don't want to do ANY cursor-based interaction when the mouse is locked
                leftData.position = new Vector2(-1.0f, -1.0f);
                leftData.delta = Vector2.zero;
            }
            else
            {
                leftData.delta = pos - leftData.position;
                leftData.position = pos;
            }
            leftData.scrollDelta = input.mouseScrollDelta;
            leftData.button = PointerEventData.InputButton.Left;
            eventSystem.RaycastAll(leftData, m_RaycastResultCache);
            var raycast = FindFirstRaycast(m_RaycastResultCache);
            leftData.pointerCurrentRaycast = raycast;
            m_RaycastResultCache.Clear();

            // copy the apropriate data into right and middle slots
            PointerEventData rightData;
            GetPointerData(kMouseRightId, out rightData, true);
            rightData.Reset();

            CopyFromTo(leftData, rightData);
            rightData.button = PointerEventData.InputButton.Right;

            PointerEventData middleData;
            GetPointerData(kMouseMiddleId, out middleData, true);
            middleData.Reset();

            CopyFromTo(leftData, middleData);
            middleData.button = PointerEventData.InputButton.Middle;

            m_MouseState.SetButtonState(PointerEventData.InputButton.Left, StateForMouseButton(0), leftData);
            m_MouseState.SetButtonState(PointerEventData.InputButton.Right, StateForMouseButton(1), rightData);
            m_MouseState.SetButtonState(PointerEventData.InputButton.Middle, StateForMouseButton(2), middleData);

            return m_MouseState;


        }
        bool _IsOnLayerMask(int layerid, int mask)
        {
            var layer = LayerMask.GetMask(LayerMask.LayerToName(layerid));

            return (layer & mask) == layer;

        }
        //使用 layer掩码 管理.....
        public bool IsOnByLayer(int mask)
        {

            PointerEventData eventData = GetLastPointerEventData(PointerInputModule.kMouseLeftId);

            //if (eventData != null && eventData.pointerDrag && eventData.pointerDrag.layer == layer)
            //{
            //   LOG.Add($"[GOTStandaloneInputModule.IsOnByLayer(pointerDrag)]：{eventData.pointerDrag}");
            //    return true;
            //}

            if (eventData != null && eventData.pointerEnter && eventData.pointerEnter.layer == mask)
            {


                if (_IsOnLayerMask(eventData.pointerEnter.layer, mask))
                    return true;
                //Debug.Log($"[GOTStandaloneInputModule.IsOnByLayer(pointerEnter)]：{eventData.pointerEnter}");

            }

            //鼠标
            var go = GetCurrentFocusedGameObject();
            if (go != null)
            {


                if (_IsOnLayerMask(go.layer, mask))
                    return true;
            }


            //触摸
            for (int i = 0; i < input.touchCount; ++i)
            {
                Touch touch = input.GetTouch(i);

                if (touch.type == TouchType.Indirect)
                    continue;

                bool released;
                bool pressed;
                var data = GetTouchPointerEventData(touch, out pressed, out released);

                if (data != null && data.pointerCurrentRaycast.gameObject != null)
                {
                    if (_IsOnLayerMask(data.pointerCurrentRaycast.gameObject.layer, mask))
                        return true;
                }


            }

            return false;
        }

        public static bool? IsInputOnGameObjectByLayer(int layer) => INSTANCE?.IsOnByLayer(layer);

        public static GameObject GETCurrentFocusedGameObject() => INSTANCE?.GetCurrentFocusedGameObject();

        public static void SETActive(bool state) => INSTANCE?.SETActive(state);

        public static GameObject GETGameObjectUnderPointer() => INSTANCE?.GameObjectUnderPointer();
    }
}
